package com.controller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.ListDataModel;

import org.apache.log4j.Logger;
import org.primefaces.event.SelectEvent;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;
import org.springframework.web.jsf.FacesContextUtils;

import com.builder.AcuerdoBuilder;
import com.builder.ProductoBuilder;
import com.builder.ProveedorBuilder;
import com.dao.BaseModelDAO;
import com.model.Acuerdo;
import com.model.Producto;
import com.model.Proveedor;
import com.query.AcuerdoQuery;
import com.type.TipoDescuentoEnum;
import com.util.JSFUtil;
import com.util.ValidatorUtil;
import com.view.AcuerdoDetalleView;
import com.view.AcuerdoView;
import com.view.ProductoView;
import com.view.ProveedorView;

@ManagedBean
@ViewScoped
@SuppressWarnings("serial")
public class AcuerdoController extends PaginableController<Acuerdo> {

	private Logger log = Logger.getLogger(AcuerdoController.class);
	private BaseModelDAO<Acuerdo> dao;
	private BaseModelDAO<Producto> productoDao;
	private BaseModelDAO<Proveedor> proveedorDao;
	private Acuerdo model;
	private AcuerdoQuery query;
	private ListDataModel<AcuerdoDetalleView> acuerdoDetalleDM;
	private List<AcuerdoDetalleView> listaAcuerdoDetalle;
	private List<ProveedorView> listaProveedor;
	private List<TipoDescuentoEnum> listaTipoDescuento;

	@ManagedProperty("#{acuerdoView}")
	private AcuerdoView view;

	@ManagedProperty("#{acuerdoDetalleView}")
	private AcuerdoDetalleView acuerdoDetalleView;

	@ManagedProperty("#{acuerdoBuilder}")
	private AcuerdoBuilder builder;

	@ManagedProperty("#{productoBuilder}")
	private ProductoBuilder productoBuilder;

	@ManagedProperty("#{proveedorBuilder}")
	private ProveedorBuilder proveedorBuilder;

	@SuppressWarnings("unchecked")
	public AcuerdoController() {

		try {
			dao = (BaseModelDAO<Acuerdo>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("acuerdoDAO");
			productoDao = (BaseModelDAO<Producto>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("productoDAO");
			proveedorDao = (BaseModelDAO<Proveedor>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("proveedorDAO");
			query = new AcuerdoQuery();
			listaAcuerdoDetalle = new ArrayList<AcuerdoDetalleView>();
			listaTipoDescuento = Arrays.asList(TipoDescuentoEnum.values());
			addEdit = false;

		} catch (Throwable e) {
			log.error("Error al inicializar la clase AcuerdoController", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@PostConstruct
	public void init() {
		listaProveedor = proveedorBuilder.toView(proveedorDao.getList());
	}

	public Acuerdo getModel() {
		return model;
	}

	public void setModel(Acuerdo model) {
		this.model = model;
	}

	public AcuerdoQuery getQuery() {
		return query;
	}

	public void setQuery(AcuerdoQuery query) {
		this.query = query;
	}

	public ListDataModel<AcuerdoDetalleView> getAcuerdoDetalleDM() {
		return acuerdoDetalleDM;
	}

	public void setAcuerdoDetalleDM(
			ListDataModel<AcuerdoDetalleView> acuerdoDetalleDM) {
		this.acuerdoDetalleDM = acuerdoDetalleDM;
	}

	public AcuerdoView getView() {
		return view;
	}

	public void setView(AcuerdoView view) {
		this.view = view;
	}

	public AcuerdoBuilder getBuilder() {
		return builder;
	}

	public void setBuilder(AcuerdoBuilder builder) {
		this.builder = builder;
	}

	public ProductoBuilder getProductoBuilder() {
		return productoBuilder;
	}

	public void setProductoBuilder(ProductoBuilder productoBuilder) {
		this.productoBuilder = productoBuilder;
	}

	public AcuerdoDetalleView getAcuerdoDetalleView() {
		return acuerdoDetalleView;
	}

	public void setAcuerdoDetalleView(AcuerdoDetalleView acuerdoDetalleView) {
		this.acuerdoDetalleView = acuerdoDetalleView;
	}

	public List<ProveedorView> getListaProveedor() {
		return listaProveedor;
	}

	public void setListaProveedor(List<ProveedorView> listaProveedor) {
		this.listaProveedor = listaProveedor;
	}

	public ProveedorBuilder getProveedorBuilder() {
		return proveedorBuilder;
	}

	public void setProveedorBuilder(ProveedorBuilder proveedorBuilder) {
		this.proveedorBuilder = proveedorBuilder;
	}

	public List<TipoDescuentoEnum> getListaTipoDescuento() {
		return listaTipoDescuento;
	}

	@Override
	public void query(ActionEvent event) {
		loadList();
	}

	@Override
	public void edit(ActionEvent event) {

		try {
			model = lazyDM.getRowData();
			model = dao.findFULL(model.getID());
			view = builder.toView(model);

			listaAcuerdoDetalle = view.getListaAcuerdoDetalle();
			acuerdoDetalleDM = new ListDataModel<AcuerdoDetalleView>(
					listaAcuerdoDetalle);
			addEdit = true;
		} catch (Throwable e) {
			log.error("Error al editar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}

	}

	@Override
	public void delete(ActionEvent event) {

		try {
			model = lazyDM.getRowData();
			dao.delete(model);
			loadList();
		} catch (Throwable e) {
			log.error("Error al eliminar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@Override
	public void add(ActionEvent event) {
		addEdit = true;
		clear();
	}

	@Override
	public void save(ActionEvent event) {

		try {
			view.setListaAcuerdoDetalle(listaAcuerdoDetalle);
			List<String> errores = new ValidatorUtil().validate(view);

			if (errores.size() == 0) {
				model = builder.toDomain(view);

				if (model.getID() != null) {
					dao.edit(model);
					addEdit = false;
				} else {
					dao.save(model);
				}
				clear();
				JSFUtil.saveMessage("Elemento guardado con exito",
						FacesMessage.SEVERITY_INFO);
				if (!addEdit) {
					loadList();
				}
			} else {
				JSFUtil.saveMessages(errores);
			}
		} catch (Throwable e) {
			log.error("Error al guardar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@Override
	public void cancel(ActionEvent event) {
		addEdit = false;
		lazyDM = null;
		clear();
	}

	@Override
	public void clear() {
		view = new AcuerdoView();
		model = new Acuerdo();
		listaAcuerdoDetalle = new ArrayList<AcuerdoDetalleView>();
		acuerdoDetalleDM = new ListDataModel<AcuerdoDetalleView>(
				listaAcuerdoDetalle);
	}

	private void loadList() {

		lazyDM = new LazyDataModel<Acuerdo>() {
			@Override
			public List<Acuerdo> load(int first, int pageSize,
					String sortField, SortOrder sortOrder,
					Map<String, Object> filters) {

				Map<String, Object> filtro = new HashMap<String, Object>();
				filtro.put("proveedor.id", query.getProveedorID());

				return dao.getList(first, pageSize, "id", true, filtro, true);
			}
		};

		Map<String, Object> filtro = new HashMap<String, Object>();
		filtro.put("proveedor.id", query.getProveedorID());
		lazyDM.setRowCount(dao.count(filtro, true).intValue());
	}

	public void addDetalle(ActionEvent event) {

		// opción todos
		if (acuerdoDetalleView.getTodos()) {
			// se agregó la opción todos?
			if (!seAgregoTodos()) {
				boolean distinto = false;
				// ver si hay descuento combinados
				for (AcuerdoDetalleView ad : listaAcuerdoDetalle) {
					for (AcuerdoDetalleView adi : listaAcuerdoDetalle) {
						if (!ad.getTipoDescuento().equals(
								adi.getTipoDescuento())) {
							distinto = true;
							break;
						}
					}

					if (distinto) {
						break;
					}
				}

				if (!distinto) {
					for (AcuerdoDetalleView ad : listaAcuerdoDetalle) {
						if (!ad.getTipoDescuento().equals(
								acuerdoDetalleView.getTipoDescuento())) {
							distinto = true;
							break;
						}
					}

					if (!distinto) {
						agregarDetalle();
					} else {
						JSFUtil.saveMessage(
								"No se puede agregar la opción 'Todos' ya que hay descuentos individuales distintos al que se pretende agregar",
								FacesMessage.SEVERITY_ERROR);
					}
				} else {
					JSFUtil.saveMessage(
							"No se puede agregar la opción 'Todos' ya que hay descuentos combinados individuales",
							FacesMessage.SEVERITY_ERROR);
				}
			} else {
				JSFUtil.saveMessage("Ya fué agregada la opción 'Todos'",
						FacesMessage.SEVERITY_ERROR);
			}
		} else {
			// veo si se agregó la opción todos y se combinan los tipos de
			// descuentos
			if (!seAgregoTodosYTieneDescuentoMixto(acuerdoDetalleView)) {
				agregarDetalle();
			}
		}
	}

	public boolean seAgregoTodosYTieneDescuentoMixto(AcuerdoDetalleView adv) {
		boolean distinto = false;

		// descuento combinados
		for (AcuerdoDetalleView ad : listaAcuerdoDetalle) {
			if (!ad.getTipoDescuento().equals(adv.getTipoDescuento())) {
				distinto = true;
				break;
			}
		}

		if (distinto) {
			if (seAgregoTodos()) {
				JSFUtil.saveMessage(
						"Ya fué agregada la opción 'Todos' por lo tanto no se puede combinar descuentos por porcentaje y descuento por cantidad",
						FacesMessage.SEVERITY_ERROR);
			} else {
				distinto = false;
			}
		}

		return distinto;

	}

	private boolean seAgregoTodos() {
		boolean ret = false;

		for (AcuerdoDetalleView ad : listaAcuerdoDetalle) {
			if (Boolean.TRUE.equals(ad.getTodos())) {
				ret = true;
				break;
			}
		}

		return ret;
	}

	public void agregarDetalle() {

		if (!listaAcuerdoDetalle.contains(acuerdoDetalleView)) {

			List<String> errores = new ValidatorUtil()
					.validate(acuerdoDetalleView);

			if (errores.size() == 0) {

				if (Boolean.TRUE.equals(acuerdoDetalleView.getTodos())) {
					listaAcuerdoDetalle.add(acuerdoDetalleView);
					acuerdoDetalleDM = new ListDataModel<AcuerdoDetalleView>(
							listaAcuerdoDetalle);
					acuerdoDetalleView = new AcuerdoDetalleView();
				} else if (acuerdoDetalleView.getProducto() != null) {
					listaAcuerdoDetalle.add(acuerdoDetalleView);
					acuerdoDetalleDM = new ListDataModel<AcuerdoDetalleView>(
							listaAcuerdoDetalle);
					acuerdoDetalleView = new AcuerdoDetalleView();

				} else {
					// debe seleccionar un producto o todos
					JSFUtil.saveMessage(
							"Se debe seleccionar un producto o todos",
							FacesMessage.SEVERITY_ERROR);

				}
			} else {
				JSFUtil.saveMessages(errores);
			}
		} else {
			// el producto existe
			JSFUtil.saveMessage("El producto ya fué agregado a la lista",
					FacesMessage.SEVERITY_ERROR);
		}
	}

	public void deleteDetalle(ActionEvent event) {

		AcuerdoDetalleView acuerdoDetalleView = acuerdoDetalleDM.getRowData();

		listaAcuerdoDetalle.remove(acuerdoDetalleView);
		acuerdoDetalleDM = new ListDataModel<>(listaAcuerdoDetalle);
	}

	public List<ProductoView> completeProducto(String query) {
		List<ProductoView> listaProducto = null;
		try {

			List<Producto> listaProductosEntity = productoDao.getList(query);
			if (listaProductosEntity != null && listaProductosEntity.size() > 0) {
				listaProducto = productoBuilder.toView(listaProductosEntity);
			}

		} catch (Throwable e) {
			log.error("Error en le metodo completeProducto: ", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
		return listaProducto;
	}

	public boolean isProductoSelected() {
		return (acuerdoDetalleView.getProducto() != null && acuerdoDetalleView
				.getProducto().getId() != null);
	}

	public void deselecionarProducto(ActionEvent event) {
		acuerdoDetalleView.setProducto(null);
	}

	public void productoSelectChange(SelectEvent event) {
		acuerdoDetalleView.setProducto((ProductoView) event.getObject());
		System.out.println("se eligio una un producto "
				+ acuerdoDetalleView.getProducto().getDetalle());
	}

	public boolean isDescuentoCantidad() {
		return TipoDescuentoEnum.CANTIDAD.equals(acuerdoDetalleView
				.getTipoDescuento());
	}

	public boolean isDescuentoPorcentaje() {
		return TipoDescuentoEnum.PORCENTAJE.equals(acuerdoDetalleView
				.getTipoDescuento());
	}

	public void cerrar(ActionEvent event) {
		acuerdoDetalleView = new AcuerdoDetalleView();
	}

	public void todosListener() {
		acuerdoDetalleView.setProducto(null);
	}

}
